<?php
namespace WDB\WebUI;
use WDB,
    WDB\Exception;

/**
 * iRequest implementation representing HTTP requests
 * 
 * @author Richard Ejem <richard(at)ejem.cz>
 * @package WDB
 */
class HttpRequest extends WDB\BaseObject implements iRequest
{
    const ACCEPT_GET = 1;
    const ACCEPT_POST = 2;
    const ACCEPT_BOTH = 3;
    
    /**@var string request identifier*/
    protected $requestId;
    /**@var int enumeration of HttpRequest::ACCEPT_* */
    protected $accept;
    /**@var Uri*/
    protected $uri;
    /**@var Uri containing table-persistent parameters*/
    protected $pUri;
    
    protected static $dynamicParams = array('state', 'selected', 'saved', 'newRecord', 'delete');
    protected static $tablePersistentParams = array('order', 'desc', 'filter');
    
    /**
     *
     * @param string request identifier
     * @param int enumeration of HttpRequest::ACCEPT_*
     */
    public function __construct($requestId = '', $accept = self::ACCEPT_BOTH)
    {
        $this->requestId = $requestId == '' ? '' : WDB\Utils\Strings::escapeVarName($requestId);
        $this->uri = new Uri($_SERVER['REQUEST_URI']);
        
        //remove url parameters from previous operations
        if ($this->requestId != '')
        {
            $params = $this->uri->getParam($this->requestId);
            $this->uri->removeParams($this->requestId);
            $this->pUri = clone $this->uri;
            if ($params !== NULL)
            {
                foreach (self::$dynamicParams as $param)
                {
                    unset($params[$param]);
                }
                $this->pUri->addParam($this->requestId, $params);
                foreach (self::$tablePersistentParams as $param)
                {
                    unset($params[$param]);
                }
                $this->uri->addParam($this->requestId, $params);
            }
        }
        else
        {
            foreach (self::$dynamicParams as $param)
            {
                $this->uri->removeParams($param);
            }
            $this->pUri = clone $this->uri;
            foreach (self::$tablePersistentParams as $param)
            {
                $this->uri->removeParams($param);
            }
        }
        switch ($accept)
        {
            case self::ACCEPT_GET:
                $this->accept = &$_GET;
                break;
            case self::ACCEPT_POST:
                $this->accept = &$_POST;
                break;
            case self::ACCEPT_BOTH:
            default:
                $this->accept = &$_REQUEST;
                break;
        }
    }
    
    public function getRequestId()
    {
        return $this->requestId;
    }
    
    // <editor-fold defaultstate="collapsed" desc="iRequest implementation">
    public function getRequestedState()
    {
        if ($this->getData($_GET, 'state')!== NULL)
        {
            return $this->getData($_GET, 'state');
        }
        else
        {
            return iTable::STATE_DEFAULT;
        }
    }
    
    public function getSaveFieldName($name)
    {
        return $this->getHttpIdentifier('data')."[$name]";
    }
    
    public function getEdited($key)
    {
        return $this->getData($this->accept, 'data', $key);
    }
    
    public function getAllEdited()
    {
        if ($this->requestId == '')
        {
            throw new Exception\InvalidOperation("Only HttpRequest with specified Request ID can call getAllEdited() ");
        }
        if (isset($this->accept[$this->requestId]))
        {
            return $this->accept[$this->requestId];
        }
        else
        {
            return NULL;
        }
    }
    
    public function getStateLink($state, $selectedRecordKey = NULL)
    {
        $args = array('state'=>$state);
        if ($selectedRecordKey !== NULL)
        {
            if ($selectedRecordKey === TRUE)
            {
                $args['newRecord'] = 1;
            }
            else
            {
                $args['selected'] = $selectedRecordKey;
            }
        }
        $u = clone $this->uri;
        $this->addUriArgs($u, $args);
        return $u;
    }
    
    public function getSelectedRecordKey()
    {
        return $this->getData($_GET, 'selected');
    }
    
    public function getEditControlName()
    {
        return $this->getHttpIdentifier('edited');
    }
    
    public function isEditSubmitted()
    {
        return (bool)$this->getData($this->accept, 'edited');
    }
    
    public function isNewRecord()
    {
        return $this->getData($_GET, 'newRecord') !== NULL;
    }
    
    public function getDeleteLink($recordKey)
    {
        $args = array('delete'=>$recordKey);
        $u = clone $this->pUri;
        $this->addUriArgs($u, $args);
        return $u;
    }
    
    public function redirectSuccessfulSave($recordKey = NULL)
    {
        $args = array('state'=>\WDB\WebUI\iTable::STATE_LIST,
            'saved'=>$recordKey);
        if ($this->requestId != '')
        {
            $args = array($this->requestId => $args);
        }
        $this->uri->redirect($args);
        
    }
    
    public function isDeleteRequest()
    {
        return $this->getData($_GET, 'delete') !== NULL;
    }
    
    public function getDeleteKey()
    {
        return $this->getData($_GET, 'delete');
    }
    
    public function getPageParamName()
    {
        return $this->getHttpIdentifier('page');
    }
    
    public function getCurrentPage()
    {
        return max(0, $this->getData($_GET, 'page')-1);
    }
    
    public function getOrderColumn()
    {
        return $this->getData($_GET, 'order');
    }
    
    public function isOrderAsc()
    {
        return !$this->getData($_GET, 'desc');
    }
    
    public function getFilters()
    {
        return $this->getData($_GET, 'filter');
    }
    
    public function configureDatasource(WDB\Wrapper\iTable $table)
    {
        $cfg = array();
        $cfg['page'] = $this->getCurrentPage();
        
        $cfg['sort'] = $this->getOrderColumn();
        $cfg['asc'] = $this->isOrderAsc();
        $cfg['filter'] = $this->getFilters();
        
        return $table->getDatasource($cfg);
    }
    
    public function orderLink($column = NULL, $asc = TRUE)
    {
        $u = clone $this->pUri;
        if ($column !== NULL)
        {
            $this->addUriArgs($u, array('order'=>$column));
        }
        else
        {
            $this->removeUriArg($u, 'order');
        }
        if ($asc)
        {
            $this->removeUriArg($u, 'desc');
        }
        else
        {
            $this->addUriArgs($u, array('desc'=>'1'));
        }
        return $u;
    }
    
    /**
     *
     * @param string $table
     * @return Uri 
     */
    public function getSelectTableLink($table)
    {
        $u = clone $this->uri;
        $this->addUriArgs($u, array('table'=>$table));
        return $u;
    }
    
    public function getSelectedTable()
    {
        return $this->getData($_GET, 'table');
    }
    
    // </editor-fold>
    
    /**
     * get data from GET/POST or its subarray if requestId is set
     *
     * @param array source array (_GET, _POST, this->accept...)
     * @param string $path,... array-level keys to get
     * @return string|null
     */
    protected function getData($source, $path)
    {
        $path = func_get_args();
        array_shift($path);
        if ($this->requestId == '')
        {
            $container = $source;
            $path[0] = $this->prefixRequestKey($path[0]);
        }
        else
        {
            if (!isset($source[$this->requestId])) return NULL;
            $container = $source[$this->requestId];
        }
        foreach ($path as $obj)
        {
            if (!isset($container[$obj])) return NULL;
            $container = $container[$obj];
        }
        return $container;
    }
    
    protected function getHttpIdentifier($name)
    {
        if ($this->requestId == '')
        {
            return $this->prefixRequestKey($name);
        }
        else
        {
            return $this->requestId.preg_replace('~[^[]+~i', '[\\0]', $name);
        }
    }
    
    protected function prefixRequestKey($key)
    {
        return 'wdb'.ucfirst($key);
    }
    
    protected function removeUriArg(Uri $uri, $arg)
    {
        if ($this->requestId || is_array($arg))
        {
            if ($this->requestId)
            {
                $p = $uri->getParam($master=$this->requestId);
            }
            else
            {
                $p = $uri->getParam($master=array_shift($arg));
            }            
            if (!is_array($arg)) $arg = array($arg);
            $rp = &$p;
            foreach ($arg as $pathElement)
            {
                if (!isset($rp[$pathElement])) return;
                
                $xp = &$rp[$pathElement];
                unset($rp);
                $rp = &$xp;
            }
            $rp = NULL;
            $uri->removeParams($master);
            $uri->addParam($master, $p);
        }
        else
        {
            $u->removeParams($this->prefixRequestKey($arg));
        }
    }
    
    protected function addUriArgs(Uri $uri, array $args)
    {
        if ($this->requestId != '')
        {
            $args = array($this->requestId=>$args);
        }
        else
        {
            $out_args = array();
            foreach ($args as $key=>$arg)
            {
                $out_args[$this->prefixRequestKey($key)] = $arg;
            }
            $args = $out_args;
        }
        $uri->addParams($args);
    }
    
    public function filterLink($column, $value)
    {
        $filters = $this->getFilters();
        $u = clone $this->pUri;
        if ($value === NULL)
        {
            $this->removeUriArg($u, array('filter', $column));
        }
        else
        {
            $this->addUriArgs($u, array('filter'=>array($column=>$value)));
        }
        
        return $u;
    }
}
